package com.zqwh.yj.sdk.model;

import android.content.Context;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;

import com.xgzx.veinmanager.VeinApi;
import com.zqwh.yj.sdk.obj.VeinUser;
import com.zqwh.yj.sdk.utils.LogUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

/**
 * Created by eason.yang on 2019/6/6.
 */

public class VeinModelImpl implements VeinModel {
    private Context mContext;
    private UsbManager mUsbManager;
    private UsbDevice mUsbDev;
    private long mDevHandle;
    private long mLibHandle;
    private SoundModel mSoundModel;
    private List<VeinUser> mVeinUsers;
    private static int mState;

    public VeinModelImpl(Context context) {
        this.mContext = context;
        mSoundModel = new SoundModelImpl(mContext);
        mVeinUsers = new ArrayList<>();
        mState = 0;
    }

    @Override
    public int searchUsbOfVein() {
        mUsbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
        HashMap<String, UsbDevice> deviceHashMap = mUsbManager.getDeviceList();
        Iterator<UsbDevice> iterator = deviceHashMap.values().iterator();

        int foundUserDev = 0;
        while (iterator.hasNext()) {
            mUsbDev = iterator.next();
            LogUtils.logout("Found device name: " + mUsbDev.getDeviceName()
                    + ", device product id:" + mUsbDev.getProductId()
                    + ", vendor id:" + mUsbDev.getVendorId());
            if ((mUsbDev.getProductId()&0xff00) == 0x7600 && (mUsbDev.getVendorId() == 0x2109 || mUsbDev.getVendorId() == 0x200d)) {
                if (mUsbManager.hasPermission(mUsbDev)) {
                    LogUtils.logout("Has USB permission...");
                }
                foundUserDev ++;
                break;
            } else {
                LogUtils.logout("No vein device, pass...");
            }
        }
        if(foundUserDev > 0) {
            LogUtils.logout("Found USB device");
        } else {
            LogUtils.error("Do not found USB device");
        }
        return foundUserDev;
    }

    @Override
    public long connectVein() {
        long ret = VeinApi.initUsbCommunication(mUsbDev, mUsbManager);
        if (ret > 0){
            mDevHandle = ret;
            String sParam = VeinApi.GetDevParam(mDevHandle);
            LogUtils.logout("Connected vein device info : "+sParam);
        }
        return ret;
    }

    @Override
    public void closeVein() {
        VeinApi.CloseDev(mDevHandle);
    }

    @Override
    public void createVeinLib(List<VeinUser> users) {
        mLibHandle = VeinApi.FVCreateVeinLib(users.size() + 600);
        if(mLibHandle <= 0) {
            LogUtils.error("算法库创建失败:"+mLibHandle);
            return;
        } else {
            LogUtils.logout("算法库创建成功:"+mLibHandle);
            for (VeinUser user : users) {
                importVeinTemp(user);
            }
        }
    }

    @Override
    public int importVeinTemp(VeinUser user) {
        mVeinUsers.add(user);
        long ret = VeinApi.FVImportVeinTemp(mLibHandle, user.getVeinId(), user.getTemp());
        if (ret == 0 && user.getUserInfo() != null){
            byte[] bUserInfo = VeinApi.StrToGBKBytes(user.getUserInfo());
            VeinApi.FVSetUserInfo(mLibHandle, user.getVeinId(), bUserInfo, bUserInfo.length);
//            LogUtils.logout("导入指静脉成功："+user.toString());
            return 0;
        }else {
//            LogUtils.error("导入指静脉失败："+user.toString());
            return -1;
        }
    }

    @Override
    public int removeVeinTemp(VeinUser user) {
        long ret = VeinApi.FVDeleteDevTemp(mLibHandle, user.getVeinId()); //删除指定ID模板
        if(ret == 0) {
            LogUtils.logout("模板删除成功: " + user.toString());
        } else {
            LogUtils.error("模板删除失败, " + ret + ": "+user.toString());
        }
        return 0;
    }

    @Override
    public void collectVeinTemp(VeinUser user, VeinCollectListener listener) {
        if (mState != 0){
            listener.onVeinCollectFinishedListener(VEIN_IN_USE, null);
            return;
        }
        mState = 1;
        int maxCharaNum = 3;
        int error = 0;
        if(mDevHandle <= 0) {
            listener.onVeinCollectFinishedListener(VEIN_DEVICE_DISCONNECTED, null);
            return;
        }

        String[] chara = new String[maxCharaNum];
        String temp = "";
        int charaNum = 0;
        mSoundModel.playVoice(SoundModel.SOUND_PUT_FINGER);

        while(true) {
            chara[charaNum] = VeinApi.GetVeinChara(mDevHandle, 6000);//通过指静脉设备采集特征，等待手指放入超时为6秒

            // 特征采集失败
            if (chara[charaNum].length() < 10) {
                mSoundModel.playVoice(SoundModel.SOUND_BB_1);
                try {
                    error = Integer.parseInt(chara[charaNum]);
                } catch (NumberFormatException e) {
                    e.printStackTrace();
                }
                if(error == -16) {
                    listener.onVeinCollectFinishedListener(VEIN_NOT_SUPPORT, null);
                    break;
                } else if(error == -11) {
                    listener.onVeinCollectFinishedListener(VEIN_OVERTIME, null);
                    break;
                } else if(error == -17) { //特征采集失败可以继续采集下一次
                    listener.onVeinCollectFinishedListener(VEIN_COLLECT_FAIL, null);
                    mSoundModel.playVoice(SoundModel.SOUND_PUT_AGAIN);
                } else {
                    listener.onVeinCollectFinishedListener(VEIN_COLLECT_FAIL, null);
                    break;
                }
            }
            // 特征采集成功
            else {
                error = 0;

                //通过增加特征的这个函数来创建模板，一个模板最多可以增加6个特征，等同于FVCreateVeinTemp
                temp = VeinApi.FVAddCharaToTemp(temp, chara[charaNum], null, 0);
                if (temp.length() < 10) {
                    mSoundModel.playVoice(SoundModel.SOUND_B_2);
                    listener.onVeinCollectFinishedListener(VEIN_ADD_CHAR_FAIL, null);
                    error = -1;
                    break;
                }

                if(charaNum == 1) {
                    //检测2次采集的特征是不是同一个手指
                    long ret = VeinApi.FVCharaMatch(chara[0], chara[1], 60);
                    if(ret != 0) {
                        mSoundModel.playVoice(SoundModel.SOUND_PUT_RIGHT);
                        listener.onVeinCollectFinishedListener(VEIN_NOT_SAME_FINGER, null);
                        error = -1;
                        break;
                    }
                }else if (charaNum == 2){
                    //检测2次采集的特征是不是同一个手指
                    long ret = VeinApi.FVCharaMatch(chara[1], chara[2], 60);
                    if(ret != 0) {
                        mSoundModel.playVoice(SoundModel.SOUND_PUT_RIGHT);
                        listener.onVeinCollectFinishedListener(VEIN_NOT_SAME_FINGER, null);
                        error = -1;
                        break;
                    }
                }

                charaNum ++;
                mSoundModel.playVoice(SoundModel.SOUND_B_1);
            }
            if(charaNum >= maxCharaNum) {
                break;
            }else if (charaNum == 1) {
                listener.onVeinCollectFinishedListener(VEIN_PUT_FINGER_AGAIN_1, null);
            }else if (charaNum == 2){
                listener.onVeinCollectFinishedListener(VEIN_PUT_FINGER_AGAIN_2, null);
            }
            mSoundModel.playVoice(SoundModel.SOUND_PUT_AGAIN);

            //等待手指拿开采集下一次
            VeinApi.CheckFinger(mDevHandle, 10000, 0);
        }
        if(error == 0) {
            LogUtils.logout("用户信息："+user.getUserInfo());
            byte[] bUserInfo = VeinApi.StrToGBKBytes(user.getUserInfo()); //用户信息有汉字，需要转换为GBK编码
            //通过采集的3个特征融合为一个模板，也可以同时导入用户信息，没有用户信息bUserInfo为null就行
            LogUtils.logout("3次特征采集完成,准备融合");
            String sTemp = VeinApi.FVCreateVeinTemp(chara[0], chara[1], chara[2], bUserInfo, bUserInfo.length);
            if (sTemp.length() > 10) {
                LogUtils.logout("模板融合成功");
                user.setTemp(sTemp);
                int ret = importVeinTemp(user);
                if (ret == 0){
                    LogUtils.logout("模板导入成功");
                    mSoundModel.playVoice(SoundModel.SOUND_ENROLL_SUCCESS);
                    listener.onVeinCollectFinishedListener(VEIN_OK, sTemp);
                }else {
                    LogUtils.error("模板导入失败");
                    mSoundModel.playVoice(SoundModel.SOUND_ENROLL_FAIL);
                    listener.onVeinCollectFinishedListener(VEIN_MIX_FAIL, null);
                }
            } else {
                mSoundModel.playVoice(SoundModel.SOUND_ENROLL_FAIL);
                listener.onVeinCollectFinishedListener(VEIN_MIX_FAIL, null);
                LogUtils.error("模板融合失败:" + sTemp);
            }
        }
        mState = 0;
    }

    @Override
    public void collectVeinTemp(VeinListener listener) {
        if (mState != 0){
            listener.onVeinCollectFinishedListener(VEIN_IN_USE, null);
            return;
        }
        mState = 1;
        int maxCharaNum = 3;
        int error = 0;
        if(mDevHandle <= 0) {
            listener.onVeinCollectFinishedListener(VEIN_DEVICE_DISCONNECTED, null);
            return;
        }

        String[] chara = new String[maxCharaNum];
        String temp = "";
        int charaNum = 0;
        mSoundModel.playVoice(SoundModel.SOUND_PUT_FINGER);

        while(true) {
            chara[charaNum] = VeinApi.GetVeinChara(mDevHandle, 6000);//通过指静脉设备采集特征，等待手指放入超时为6秒

            // 特征采集失败
            if (chara[charaNum].length() < 10) {
                mSoundModel.playVoice(SoundModel.SOUND_BB_1);
                try {
                    error = Integer.parseInt(chara[charaNum]);
                } catch (NumberFormatException e) {
                    e.printStackTrace();
                }
                if(error == -16) {
                    listener.onVeinCollectFinishedListener(VEIN_NOT_SUPPORT, null);
                    break;
                } else if(error == -11) {
                    listener.onVeinCollectFinishedListener(VEIN_OVERTIME, null);
                    break;
                } else if(error == -17) { //特征采集失败可以继续采集下一次
                    listener.onVeinCollectFinishedListener(VEIN_COLLECT_FAIL, null);
                    mSoundModel.playVoice(SoundModel.SOUND_PUT_AGAIN);
                } else {
                    listener.onVeinCollectFinishedListener(VEIN_COLLECT_FAIL, null);
                    break;
                }
            }
            // 特征采集成功
            else {
                error = 0;

                //通过增加特征的这个函数来创建模板，一个模板最多可以增加6个特征，等同于FVCreateVeinTemp
                temp = VeinApi.FVAddCharaToTemp(temp, chara[charaNum], null, 0);
                if (temp.length() < 10) {
                    mSoundModel.playVoice(SoundModel.SOUND_B_2);
                    listener.onVeinCollectFinishedListener(VEIN_ADD_CHAR_FAIL, null);
                    error = -1;
                    break;
                }

                if(charaNum == 1) {
                    //检测2次采集的特征是不是同一个手指
                    long ret = VeinApi.FVCharaMatch(chara[0], chara[1], 60);
                    if(ret != 0) {
                        mSoundModel.playVoice(SoundModel.SOUND_PUT_RIGHT);
                        listener.onVeinCollectFinishedListener(VEIN_NOT_SAME_FINGER, null);
                        error = -1;
                        break;
                    }
                }else if (charaNum == 2){
                    //检测2次采集的特征是不是同一个手指
                    long ret = VeinApi.FVCharaMatch(chara[1], chara[2], 60);
                    if(ret != 0) {
                        mSoundModel.playVoice(SoundModel.SOUND_PUT_RIGHT);
                        listener.onVeinCollectFinishedListener(VEIN_NOT_SAME_FINGER, null);
                        error = -1;
                        break;
                    }
                }

                charaNum ++;
                mSoundModel.playVoice(SoundModel.SOUND_B_1);
            }
            if(charaNum >= maxCharaNum) {
                break;
            }else if (charaNum == 1) {
                listener.onVeinCollectFinishedListener(VEIN_PUT_FINGER_AGAIN_1, null);
            }else if (charaNum == 2){
                listener.onVeinCollectFinishedListener(VEIN_PUT_FINGER_AGAIN_2, null);
            }
            mSoundModel.playVoice(SoundModel.SOUND_PUT_AGAIN);

            //等待手指拿开采集下一次
            VeinApi.CheckFinger(mDevHandle, 10000, 0);
        }
        if(error == 0) {
            listener.onVeinCollectFinishedListener(VEIN_COLLECT_FINISHED, chara);
        }
        mState = 0;
    }

    public String createVeinTemp(VeinUser user, String[] chara){
        LogUtils.logout("用户信息："+user.getUserInfo());
        byte[] bUserInfo = VeinApi.StrToGBKBytes(user.getUserInfo()); //用户信息有汉字，需要转换为GBK编码
        //通过采集的3个特征融合为一个模板，也可以同时导入用户信息，没有用户信息bUserInfo为null就行
        LogUtils.logout("3次特征采集完成,准备融合");
        String sTemp = VeinApi.FVCreateVeinTemp(chara[0], chara[1], chara[2], bUserInfo, bUserInfo.length);
        if (sTemp.length() > 10) {
            LogUtils.logout("模板融合成功");
            user.setTemp(sTemp);
            int ret = importVeinTemp(user);
            if (ret == 0){
                LogUtils.logout("模板导入成功");
                mSoundModel.playVoice(SoundModel.SOUND_ENROLL_SUCCESS);
                return sTemp;
            }else {
                LogUtils.error("模板导入失败");
                mSoundModel.playVoice(SoundModel.SOUND_ENROLL_FAIL);
                return null;
            }
        } else {
            mSoundModel.playVoice(SoundModel.SOUND_ENROLL_FAIL);
            LogUtils.error("模板融合失败:" + sTemp);
            return null;
        }
    }

    @Override
    public void search(VeinSearchListener listener) {
        if (mState != 0){
            listener.onVeinSearchFinishedListener(VEIN_IN_USE, -1, null);
            return;
        }
        mState = 1;
        if(mDevHandle <= 0) {
            listener.onVeinSearchFinishedListener(VEIN_DEVICE_DISCONNECTED, -1, null);
            return;
        }
        mSoundModel.playVoice(SoundModel.SOUND_PUT_FINGER);
        String chara = VeinApi.GetVeinChara(mDevHandle, 3000);
        if(chara.length() < 10) {
            int ret = 0;
            try {
                ret = Integer.parseInt(chara);
            } catch (NumberFormatException e) {
                e.printStackTrace();
            }
            mSoundModel.playVoice(SoundModel.SOUND_B_2);
            if (ret == -16){
                LogUtils.error("此设备不支持特征采集");
                listener.onVeinSearchFinishedListener(VEIN_NOT_SUPPORT, -1, null);
            }else if (ret == -11){
                LogUtils.error("手指检测超时");
                listener.onVeinSearchFinishedListener(VEIN_OVERTIME, -1, null);
            }else if (ret == -17){
                LogUtils.error("请正确放置手指");
                listener.onVeinSearchFinishedListener(VEIN_COLLECT_FAIL, -1, null);
                mSoundModel.playVoice(SoundModel.SOUND_PUT_RIGHT);
            }else {
                LogUtils.error("特征采集失败:"+chara);
                listener.onVeinSearchFinishedListener(VEIN_COLLECT_FAIL, -1, null);
            }
        }else {
            long userId = VeinApi.FVSearchUser(mLibHandle, chara, 2);
            if (userId > 0){
                byte[] bUserInfo = new byte[2000];
                long ret = VeinApi.FVGetUserInfo(mLibHandle, userId, bUserInfo);
                if(ret > 0) {
                    mSoundModel.playVoice(SoundModel.SOUND_B_1);
                    String userInfo = VeinApi.BytestoGBKStr(bUserInfo, (int) ret);
                    listener.onVeinSearchFinishedListener(VEIN_OK, userId, userInfo);
                }else {
                    mSoundModel.playVoice(SoundModel.SOUND_B_2);
                    listener.onVeinSearchFinishedListener(VEIN_SEARCH_FAIL, userId, null);
                }
            }else {
                mSoundModel.playVoice(SoundModel.SOUND_B_2);
                listener.onVeinSearchFinishedListener(VEIN_SEARCH_FAIL, userId, null);
            }
        }
        mState = 0;
    }

    @Override
    public void resetState() {
        mState = 0;
    }

    @Override
    public void addVeinUser(VeinUser user) {
        mVeinUsers.add(user);
    }

    @Override
    public void removeUserVein(String userId, int veinNo){
        VeinUser deleteUser = null;
        for (VeinUser veinUser: mVeinUsers){
            if (userId.equals(veinUser.getUserId()) && veinNo == veinUser.getVeinNo()){
                removeVeinTemp(veinUser);
                deleteUser = veinUser;
            }
        }
        if (deleteUser != null){
            mVeinUsers.remove(deleteUser);
        }
    }

    public long getCurrentVeinId(){
        long currentVeinId = 0;
        for (VeinUser veinUser:mVeinUsers){
            if (currentVeinId < veinUser.getVeinId()){
                currentVeinId = veinUser.getVeinId();
            }
        }
        return currentVeinId + 1;
    }
}
